#include <iostream>
#include <luaglue/luaglue.hpp>

class person
{
    public:
        person(const std::string &name)
            : m_name(name)
        {
        }

        void greet(void)
        {
            std::cout << "Hi, I am " << m_name << std::endl;
        }

        inline std::string name()
        {
            return m_name;
        }

        virtual ~person()
        {
            std::cout << __func__ << ": "<< m_name << " is been release!" <<std::endl;
        }

    private:
        std::string m_name;
};

class man: public person
{
    public:
        man(const std::string &name)
            : person(name)
        {
        }

        void greet(void)
        {
            std::cout << "Hi, I am a man, named " << name() << std::endl;
        }
};

static inline void execute(lua_State *L, const std::string &state)
{
    std::cout << "> " << state << std::endl;

    if (luaL_dostring(L, state.c_str()))
    {
        std::cout << lua_error(L) << std::endl;
        lua_pop(L, 1);
    }
}

static void demon(lua_State *L)
{
    luaglue::scope(L)
    [
        luaglue::class_<person, person, person>("person")
        .constructor<const std::string &>("new")
        .def("greet", &person::greet),

        luaglue::class_<man, person, person>("man")
        .constructor<const std::string &>("new")
    ];

    luaglue::object global = luaglue::object::global(L);

    std::cout << "-- Export a person object to lua ...\n";
    shared_ptr<person> p(new person("PER"));
    global["p"] = p;
    execute(L, "p:greet()");

    std::cout << "\n-- Refence object from lua ...\n";
    execute(L, "m = man.new[[MAN]]");
    luaglue::object obj(global["m"]);
    obj.push();
    luaglue::object obj_m(obj.vm(), -1);
    lua_pop(L, 1);
    luaglue::holder<shared_ptr<person> > hld_m = obj_m.cast<shared_ptr<person> >();
    if (hld_m)
    {
        shared_ptr<person> m = *hld_m;
        m->greet();
    }
    else
    {
        std::cout << "_G.m is not a object of person!!!\n";
    }

    std::cout << "\n--Release p at C++...\n";
    p.reset();
    std::cout << "--Release p at lua...\n";
    execute(L, "p = nil");
    execute(L, "collectgarbage()");

    std::cout << "\n--Release m at lua...\n";
    execute(L, "m = nil");
    execute(L, "collectgarbage()");

    std::cout<< "----- leave " <<  __func__ << " -----"<< std::endl;
}

int main()
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    demon(L);
    lua_close(L);

    return 0;
}

